
--[[
*********************************************************************************
** 
** Copyright 2021-2024 LuaOS
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
** SOFTWARE.
** 
*********************************************************************************
]]--

----------------------------------------------------------------------------

local insert    = table.insert;
local concat    = table.concat;
local getlocal  = debug.getlocal;
local tostring  = tostring;

----------------------------------------------------------------------------

local escape = function()
  local r = {}
  local t = {"$", "(", ")", "%", ".", "[", "]", "*", "+", "-", "?"};
  for k, v in ipairs(t) do r[v] = k end
  return r;
end

escape = escape();

----------------------------------------------------------------------------

local function getenv(env, lv)
  local i = 1;
  env = setmetatable(env, {__index = _G});
  while true do
    local n, v = getlocal(lv, i);
    if not n then break end
    if not env[n] then env[n] = v end
    i = i + 1;
  end
  return env;
end

----------------------------------------------------------------------------

local function pattern(s)
  local r, c = {};
  for i = 1, #s do
    c = s:sub(i, i);
    if escape[c] then insert(r, "%") end
    insert(r, c);
  end
  return concat(r);
end

----------------------------------------------------------------------------

local function content(s)
  s = s:gsub("%%", "%%%%");
  return s;
end

----------------------------------------------------------------------------

local function escape_braces(s)
  return s:gsub(pattern("({"), "\1"):gsub(pattern("})"), "\2");
end

----------------------------------------------------------------------------

local function unescape_braces(s)
  return s:gsub("\1", "{"):gsub("\2", "}");
end

----------------------------------------------------------------------------

local function format(s, env)
  env = env or {};
  env = getenv(env, 3);
  s = escape_braces(s);
  for k in s:gmatch("%b{}") do
    local x = k:sub(2, -2);
    if x:match("%b{}") then
	  s = s:gsub(pattern(k), format(x, env));
	else
      local f = load("return " .. x, "", "t", env);
      s = s:gsub(pattern(k), content(tostring(f())));
	end
  end
  s = unescape_braces(s);
  return s;
end

----------------------------------------------------------------------------

return format;

----------------------------------------------------------------------------
